package com.smarttest.common.util.protocol;


import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.smarttest.common.beans.protolcol.Index;


public class ByteUtil {
	private static Logger logger = LoggerFactory.getLogger(ByteUtil.class);
	
	public static final String CHARSET_UCS2 = "UTF-16BE";
	/**
	 * 解析字节
	 * @param data	消息体
	 * @param index	消息体当前下标
	 * @return
	 */
	public static byte parseByte(byte[] data,Index index){
		return data[index.getAndAdd()];
	}

	/**
	 * MSB为符号位
	 */
	public static int parseByte2IntSign(byte[] data,Index index){
		int sign = ((data[index.get()] & 0xff)>>7 == 1) ? -1:1;
		return (data[index.getAndAdd()]&0x7f)*sign;
	}
	/**
	 * MSB为符号位
	 */
	public static long parseDWord2LongSign(byte[] data,Index index){
		int sign = ((data[index.get()] & 0xff)>>7 == 1) ? -1:1;
		long value= ((long)(data[index.getAndAdd()] & 0x7f)<<24 )+((data[index.getAndAdd()] & 0xff)<<16)+((data[index.getAndAdd()] & 0xff)<<8 )+(data[index.getAndAdd()] & 0xff);
		return value*sign;
	}
	
	public static int parseByte2Int(byte[] data,Index index){
		return data[index.getAndAdd()] & 0xff;
	}
	
	public static int parseWord(byte[] data,Index index){
		return ((data[index.getAndAdd()] & 0xff) << 8) + (data[index.getAndAdd()] & 0xff);
	}
	
	/**
	 * MSB为符号位
	 */
	public static int parseWordSign(byte[] data,Index index){
		int sign = ((data[index.get()] & 0xff)>>7 == 1) ? -1:1;
		return (((data[index.getAndAdd()]&0x7f)<< 8) +(data[index.getAndAdd()]&0xff))*sign;
	}
	
	
	public static long parseDWord(byte[] data,Index index){
		return ((long)(data[index.getAndAdd()] & 0xff)<<24 )+((data[index.getAndAdd()] & 0xff)<<16)+((data[index.getAndAdd()] & 0xff)<<8 )+(data[index.getAndAdd()] & 0xff);
	}
	
	/**
	 * @param data	消息体
	 * @param index	当前下标
	 * @param dstArrLen	目的数组长度
	 * @return
	 */
	public static byte[] arrayCopy(byte[] data, Index index,int dstArrLen) {
		byte[] byteArr = new byte[dstArrLen];
		System.arraycopy(data, index.get(), byteArr, 0, byteArr.length);
		index.add(byteArr.length);
		return byteArr;
	}
	/**
	 * 拷贝字符串类型的字节数组,字符串都是后面有个0,前面的长度字段包含了这个0
	 * @param data	消息体
	 * @param index	当前下标
	 * @param dstArrLen	目的数组长度
	 * @return
	 */
	public static byte[] arrayCopyString(byte[] data, Index index,int dstArrLen) {
		//最后一个字节取出来但是不解析
		byte[] byteArr = new byte[dstArrLen];
		System.arraycopy(data, index.get(), byteArr, 0, dstArrLen);
		index.add(dstArrLen);
		return byteArr;
	}
	public static void putByte(byte[] data,int value,Index index){
		data[index.getAndAdd()] = (byte) (value & 0xff);
	}
	
	public static void putWord(byte[] data,int value,Index index){
		data[index.getAndAdd()]=(byte)((value >> 8) & 0xFF);
		data[index.getAndAdd()]=(byte)(value & 0xFF);
	}
	
	public static void putDword(byte[] data,long value,Index index){
		data[index.getAndAdd()]=(byte) ((value>>24) & 0xFF);
		data[index.getAndAdd()]=(byte)((value >> 16) & 0xFF);
		data[index.getAndAdd()]=(byte)((value >> 8) & 0xFF);
		data[index.getAndAdd()]=(byte)(value & 0xFF);
	}
	public static void putStringByteArr(byte[] data,byte[] value,Index index){
		for (byte b : value) {
			data[index.getAndAdd()] = b;
		}
		data[index.getAndAdd()] = 0;
	}
	
	public static int bcdDec(byte b) {
		//b必须先转化为无符号数
		return Integer.parseInt(Integer.toString((b & 0xff), 16), 10);
	}
	public static int bcdDec(byte[] data ,Index index) {
        //b必须先转化为无符号数
        return Integer.parseInt(Integer.toString((data[index.getAndAdd()] & 0xff), 16), 10);
    }
	
	/**
	 * 最大值不能超过99
	 * @param value
	 * @return
	 */
	public static byte bcdEnc2Byte(int value){
		return (byte)Integer.parseInt(Integer.toString((value & 0xff)), 16);
	}
	
	/**
	 * 最大值不能超过99,16进制的100的值为256已经超过一个无符号byte的最大值
	 * @param value
	 * @return
	 */
	public static int bcdEnc(int value){
		return Integer.parseInt(Integer.toString((value & 0xff)), 16);
	}
	
	/**
	 * 将日期的bcd格式放入指定数组的指定位置
	 * @param cal
	 * @param data
	 * @param start
	 * @return
	 */
	public static byte[] calendar2bcd(Calendar cal,byte[] data,int start) {
		String str = String.valueOf(cal.get(Calendar.YEAR) - 2000);
		data[start++] = (byte) Integer.parseInt(str, 16);
		
		str = cal.get(Calendar.MONTH) + 1 + "";
		data[start++] = (byte) (Integer.parseInt(str, 16));
		
		str = cal.get(Calendar.DAY_OF_MONTH) + "";
		data[start++] = (byte) Integer.parseInt(str, 16);
		
		str = cal.get(Calendar.HOUR_OF_DAY) + "";
		data[start++] = (byte) Integer.parseInt(str, 16);
		
		str = cal.get(Calendar.MINUTE) + "";
		data[start++] = (byte) Integer.parseInt(str, 16);
		
		str = cal.get(Calendar.SECOND) + "";
		data[start++] = (byte) Integer.parseInt(str, 16);

		return data;
	}
	
	public static Date bcd2Date(byte[] data, Index index) {
		//采集时间
		Calendar calendar = Calendar.getInstance();
		calendar.set(2000 + ByteUtil.bcdDec(data[index.getAndAdd()]), ByteUtil.bcdDec(data[index.getAndAdd()]) - 1, ByteUtil.bcdDec(data[index.getAndAdd()])
				, ByteUtil.bcdDec(data[index.getAndAdd()]), ByteUtil.bcdDec(data[index.getAndAdd()]), ByteUtil.bcdDec(data[index.getAndAdd()]));
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTime();
	}
	
	/**
	 * @param data
	 * @param index
	 * @param 占多少长度
	 * @return
	 */
	public static String getString(byte[] data, Index index, int len){
		String result = null;
		if(len < 1){
			return null;
		}
		//所有的字符串都是后面有个0，前面的长度字段包含了这个0
		try {
			result = new String(arrayCopyString(data, index, len), "GBK");
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
		}
		return result;
	}
	
	/**
	 * @param data
	 * @param index
	 * @param 占多少长度
	 * @return
	 */
	public static String getStringUCS2(byte[] data, Index index, int len){
		String result = null;
		if(len == 0){
			return "";
		}
		//所有的字符串都是后面有个0，前面的长度字段包含了这个0
		try {
			result = new String(arrayCopy(data, index, len), CHARSET_UCS2);
		} catch (UnsupportedEncodingException e) {
			logger.error(e.getMessage(), e);
		}
		return result;
	}
	public static short  CRC16Caculate(byte[] AP, int j){
	    short  CRC16;
	    int  i;
	    byte  CRC_H4 = 0;
	    short  CRCTable[] ={0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7, (short) 0x8108,(short) 0x9129,(short) 0xA14A,(short) 0xB16B,(short) 0xC18C,(short) 0xD1AD,(short) 0xE1CE,(short) 0xF1EF};
	    CRC16= (short) (0xFFFF - (j&0xFFFF));

	    for (i = 0; i <AP.length; i++){
			    try{
			        CRC_H4 = (byte) ((CRC16 >> 12)&0x0F);
			        CRC16 = (short) (CRC16 << 4);
			        CRC16 = (short) (CRC16 ^ CRCTable[CRC_H4 ^ ((AP[i] >> 4)&0x0F)]);
			        CRC_H4 = (byte) ((CRC16 >> 12)&0x0F);
			        CRC16 = (short) (CRC16 << 4);
			        CRC16 = (short) (CRC16 ^ CRCTable[CRC_H4 ^ (AP[i] & 0x0F)]);        
			    }catch(Exception e){
			    	System.out.println(CRC_H4 + "" +((AP[i] >> 4)&0x0F));
			    }
		    }

	    return CRC16;
	}
	
	/**
     * 获取报文信息
     * @param message
     * @return
     */
    public static String getMessageStr(byte[] message) {
        String result = "";
        char[] chArr = Hex.encodeHex(message);
        for (int i = 0; i < chArr.length; i++) {
            result += String.valueOf(chArr[i]).toUpperCase();
            if (i % 2 != 0) {
                result += " ";
            }
        }
        return result;
    }
    
    public static byte[] getBytes(String data) {
        return getBytes(data, "GBK");
    }
    
    public static byte[] getBytes(String data, String charsetName) {
        Charset charset = Charset.forName(charsetName);
        return data.getBytes(charset);
    }
	
    public static int parseCanByte2Int(byte[] in,Index index){
		int value = in[index.add()] & 0xff;
		return value == 0xFF ? -1 : value;
	}
	
	public static int parseCanWord(byte[] in,Index index){
		int value = ((in[index.add()] & 0xff) << 8) + (in[index.add()] & 0xff);
		return value == 0xFFFF ? -1 : value;
	}
	
	public static long parseCanDWord(byte[] in,Index index){
		long value = (((in[index.add()] & 0xff)<<24 )+((in[index.add()] & 0xff)<<16)+((in[index.add()] & 0xff)<<8 )+(in[index.add()] & 0xff)) & 0xFFFFFFFFL;
		return value == 0xFFFFFFFFL ? -1 : value;
	}
	
	/**
	 * 计算带带单位后的值
	 * @author Lyn(tanqunlin@huoyunren.com)
	 * @date 2015年2月3日 上午10:13:37
	 * @param src
	 * @param unit
	 * @return src==-1返回-1; else 返回 原始值*单位
	 */
	public static double getFormatedVal(double src, double unit){
		int length=2;
		if(unit==0.000001){
			length=7;
		}
		return ByteUtil.getFormatedVal(src,unit,length);
	}
	
	/**
	 * @param src
	 * @param unit  
	 * @param length 精度
	 * @return
	 */
	public static double getFormatedVal(double src, double unit,int length){
		if (src == -1) {
			return src;
		}
		if(length<1||length>20){
			length=2;
		}
		BigDecimal b = new BigDecimal(src * unit);
		return b.setScale(length, BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	
	/**获得硬件版本
	 * @param in
	 * @param index
	 * @return
	 */
	public static String parseHardVer(byte[] in,Index index) {
		 StringBuilder sb = new StringBuilder("v");
		 sb.append(Integer.toString(in[index.add()] & 0xFF, 16));
		 return sb.insert(2, '.').toString();
		
	}
	/**获得软件版本
	 * @param in
	 * @param index
	 * @return
	 */
	public static String parseSoftVer(byte[] in,Index index) {
	    StringBuilder sb = new StringBuilder("v");
	    sb.append(in[index.add()] & 0xFF).append(".").append(in[index.add()] & 0xFF).append(".").
	    append(in[index.add()] & 0xFF).append(".").append(in[index.add()] & 0xFF);
	        return sb.toString();
	    }
	private static final double POWER_6 = Math.pow(10, 6);
	/**纬度
	 * @param data
	 * @param index
	 * @return
	 */
	public static double parseLat(byte[] data,Index index){
		double lat = ((double)(parseDWord(data, index))) / POWER_6;
		 return lat;
	}
	/**经度
	 * @param data
	 * @param index
	 * @return
	 */
	public static double parseLng(byte[] data,Index index){
		double lng = ((double)(parseDWord(data, index))) / POWER_6;
	    return lng;
	}
	
	/**
	 * @param data
	 * @param index
	 * @param len 占多少长度
	 * @return
	 */
	public static String parseByte2Bcd(byte[] data, Index index, int len){
		StringBuilder result = new StringBuilder();
		for(int i=0;i<len;i++){
			int temp=bcdDec(data[index.getAndAdd()]);
			if(temp<10){
				result.append("0");
			}
			result.append(temp);
		}
		return result.toString();		
	}
	// 大端模式
	public static byte[] parseLongToBytes(long data) {
		byte[] bytes = new byte[8];
		bytes[0] = (byte) ((data >> 56) & 0xff);
		bytes[1] = (byte) ((data >> 48) & 0xff);
		bytes[2] = (byte) ((data >> 40) & 0xff);
		bytes[3] = (byte) ((data >> 32) & 0xff);
		bytes[4] = (byte) ((data >> 24) & 0xff);
		bytes[5] = (byte) ((data >> 16) & 0xff);
		bytes[6] = (byte) ((data >> 8) & 0xff);
		bytes[7] = (byte) (data & 0xff);
		return bytes;
	}
// 大端模式
	public static long parseBytesToLong(byte[] bytes,Index index) {
		long value= 
				 (0xff00000000000000L & ((long) bytes[index.getAndAdd()] << 56))
				| (0xff000000000000L & ((long) bytes[index.getAndAdd()] << 48))
				| (0xff0000000000L & ((long) bytes[index.getAndAdd()] << 40))
				| (0xff00000000L & ((long) bytes[index.getAndAdd()] << 32))
				| (0xff000000L & ((long) bytes[index.getAndAdd()] << 24))
				| (0xff0000L & ((long) bytes[index.getAndAdd()] << 16))
				| (0xff00L & ((long) bytes[index.getAndAdd()] << 8))
				| (0xffL & (long) bytes[index.getAndAdd()]) ;
		return value;
	}

	/**
	 * 转化成正数BigInteger
	 * @param bytes
	 * @param index
	 * @param length
	 * @return
	 */
	public static final BigInteger parseByteToBigInteger(byte[] bytes,Index index,int length) {
		 byte[] data=new byte[length];
		 int startPos=index.get();
		 System.arraycopy(bytes,startPos, data, 0, length);
		 index.add(length);
		 BigInteger bigInteger=new BigInteger(1,data);		     
	     return bigInteger;
	}	
	public static byte[] parseString2Bcd(String data,int length){
		byte [] value=new byte[length];
		int j=0;
		for(int i=0;i<length*2;i++){
			if(i%2==0){
				String tmepStr=data.substring(i, i+2);
				value[j++]=(byte) Integer.parseInt(tmepStr,16);
			}
			
		}
		return value;
	}
	
	/**
	 * @param data
	 * @param index
	 * @return
	 */
	public static String getIdentityCardID(byte[] data, Index index){
		StringBuilder result = new StringBuilder();		
		for(int i=0;i<8;i++){
			int temp=bcdDec(data[index.getAndAdd()]);
			if(temp<10){
				result.append("0");
			}
			result.append(temp);
		}
      // 最后一位为X的用16进制’A’表示 
		if((data[index.get()]&0x0F)==0x0A){
			result.append(data[index.getAndAdd()]>>4&0x0f);
			result.append("X");
		}else{			
			int temp=bcdDec(data[index.getAndAdd()]);
			if(temp<10){
				result.append("0");
			}
			result.append(temp);
		}
		return result.toString();		
	}
	
	public static byte[] parseIdentityCardID(String data){
		if(data==null||data.length()!=18) return null;
		if(data.endsWith("X")){
			data=data.replaceAll("X", "A");		
		}
		return parseString2Bcd(data,9);		
	}
	
	public static byte[] parseDriverNoToByte(String value) {
		String regex="^[0-9A-Fa-f]*$";
		if(value!=null&&value.matches(regex)&&value.length()==32){
			byte[] valueBy=new byte[16];
			int j=0;
			for(int i=0;i<value.length();i+=2){
				String temp=value.substring(i, i+2);
				valueBy[j++]=(byte)Integer.parseInt(temp,16);
			}
			return valueBy;
		}
		return null;
	}
	
	 /**
     * 解析附加信息
     * 
     * @param data
     * @param index
     * @return
     */
	public static Map<Integer, byte[]> parsePlvAdditionInfo(byte[] data, int index) {
        Map<Integer, byte[]> additionInfo = new HashMap<Integer, byte[]>();
        int cnt = 0;
        while (index+1 < data.length && cnt<100) {
            //PLV ID
            int id =  ((data[index++] & 0xff) << 8) + (data[index++] & 0xff);
            
            //PLV length
            int length =data[index++] & 0xff;
            
            //防止错误报文导致长度越界
            if (length==0 || index + length > data.length) {
                break;
            }
            
            //PLV value
            byte[] result = new byte[length];
            System.arraycopy(data, index, result, 0, length);
            additionInfo.put(id, result);
            
            //Next PLV
            index = index + length;
            cnt++;
        }
        return additionInfo;
    }
	
	/**
	 * 最高位为标识位，
	 * 标识位=1，代表此字节的后7位和下一个字节一起表示长度。
	 * 标识位=0， 代表此字节的后7位表示长度。
	 * @param data
	 * @param index
	 * @return
	 */
	public static int getLength(byte[] data,Index index){
    	int result=0;
    	int flagBit=(data[index.get()] & 0xff)>>7;
        if(flagBit==1){
        	result=((data[index.getAndAdd()] & 0x7f)<<8)+(data[index.getAndAdd()] & 0xff);
        }else{
        	result=data[index.getAndAdd()] & 0x7f;
        }
        return result;
    }
    
	public static void main(String[] args) {
		System.out.println(Hex.encodeHexString(ByteUtil.calendar2bcd(Calendar.getInstance(), new byte[6], 0)));
	}
}
